from jose import jwt, JWTError
import datetime
from typing import Dict, Optional

# JWT配置
SECRET_KEY = "myproject_secret_key"  # 实际项目中应该从配置文件读取
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
REFRESH_TOKEN_EXPIRE_DAYS = 7


def create_access_token(data: dict, expires_delta: Optional[datetime.timedelta] = None) -> str:
    """
    创建访问令牌 (access token)
    :param data: 要编码的数据
    :param expires_delta: 过期时间
    :return: JWT token
    """
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.datetime.utcnow() + expires_delta
    else:
        expire = datetime.datetime.utcnow() + datetime.timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def create_refresh_token(data: dict, expires_delta: Optional[datetime.timedelta] = None) -> str:
    """
    创建刷新令牌 (refresh token)
    :param data: 要编码的数据
    :param expires_delta: 过期时间
    :return: JWT refresh token
    """
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.datetime.utcnow() + expires_delta
    else:
        expire = datetime.datetime.utcnow() + datetime.timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def decode_token(token: str) -> Dict:
    """
    解码JWT token
    :param token: JWT token
    :return: 解码后的数据
    """
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except JWTError as e:
        if "expired" in str(e).lower():
            raise Exception("Token已过期")
        else:
            raise Exception("无效的Token")


def refresh_access_token(refresh_token: str) -> str:
    """
    使用刷新令牌获取新的访问令牌
    :param refresh_token: 刷新令牌
    :return: 新的访问令牌
    """
    payload = decode_token(refresh_token)
    # 移除旧的exp字段
    payload.pop("exp", None)
    # 创建新的访问令牌
    new_access_token = create_access_token(data=payload)
    return new_access_token